#include <stdio.h>
#include <stdlib.h>
#include "camusb.h"
#include "filterapi.h"
#include "os.h"

#define FILTER_USE_JAVA_USBAPI 1
#define FILTER_USE_LOCAL_FILE 2
#define FILTER_USE_NATIVE_FD 3

#define FILTER_READTS_WAY FILTER_USE_NATIVE_FD

#define FILTERPORT_DEBUG(fmt, ...) //SM_Printf(fmt, ##__VA_ARGS__)
static int s32DevId = 0;

#if FILTER_READTS_WAY==FILTER_USE_JAVA_USBAPI
unsigned int Filter_ReadTS(unsigned char* pu8TSBuf, unsigned int u32Length)
{
	static FILE* pF = NULL;
	unsigned int u32Size = 0;
	int s32Ret;
	static unsigned char u8Init = 0;

	if(u8Init == 0)
	{
		u8Init = 1;
	}

	s32DevId = OpenCamUsb();
	if(s32DevId && s32DevId != -1)
	{
		do
		{
			u32Size = u32Length;
			FILTERPORT_DEBUG("Filter_ReadTS UsbReadTSData entry\r\n");
			s32Ret = UsbReadTSData(s32DevId, pu8TSBuf, &u32Size, 1000);
			Filter_RecordWorking(pu8TSBuf, u32Size);	
			FILTERPORT_DEBUG("Filter_ReadTS UsbReadTSData return\r\n", pF);
		}while(s32Ret == -1);

	}
	else
	{
		FILTERPORT_DEBUG("Filter_ReadTS Find dev id invalid\r\n");
		SM_OsThreadSleep(50);
		u32Length = 0;
	}

	FILTERPORT_DEBUG("Filter_ReadTS return, s32Size = %d, s32Ret = %d\r\n", u32Size, s32Ret);
	return u32Size;
}

#elif FILTER_READTS_WAY==FILTER_USE_LOCAL_FILE

unsigned int Filter_WriteTSTask(void* pParam)
{
	static FILE* pF = NULL;
	unsigned char au8Buffer[1880];
	unsigned int s32Size = 0;
	int s32Id;

	FILTERPORT_DEBUG("Filter_WriteTSTask entry\r\n");

	if(!pF)
	{
		pF = fopen("/sdcard/MP2_1920x1080i_AC3_Philips_ColorsOfMiami.ts", "rb");
		FILTERPORT_DEBUG("Filter_WriteTSTask open file, pF = %p\r\n", pF);
	}

	while(1)
	{
		if(feof(pF))
		{
			fseek(pF, 0, SEEK_SET);
		}
		s32Size = fread(au8Buffer, 1, 188, pF);
		s32Id = OpenCamUsb();
		s32Size = UsbWriteTSData(s32Id, au8Buffer, s32Size, -1);
		SM_OsThreadSleep(1);
		FILTERPORT_DEBUG("Filter_WriteTSTask write %d bytes\r\n", s32Size);
	}
	return 0;
}

int Filter_WriteTSTaskStart()
{
	unsigned int hThreadId;
	SM_Printf("Filter_WriteTSTaskStart call\r\n");
	SM_OsThreadCreate(0, Filter_WriteTSTask, NULL, &hThreadId);
	SM_Printf("Filter_WriteTSTaskStart SM_OsThreadCreate return\r\n");
	return 0;
}

unsigned int Filter_ReadTS(unsigned char* pu8TSBuf, unsigned int u32Length)
{
	static FILE* pF = NULL;
	unsigned int u32Size = 0;
	static unsigned char u8Init = 0;

	if(u8Init == 0)
	{
		u8Init = 1;
	}

	FILTERPORT_DEBUG("Filter_ReadTS entry, pu8TSBuf = %p, u32Length = %d\r\n", pu8TSBuf, u32Length);
	if(!pF)
	{
		pF = fopen("/mnt/sda/sda/MP2_1920x1080i_AC3_Philips_ColorsOfMiami.ts", "rb");
		FILTERPORT_DEBUG("Filter_ReadTS open file, pF = %p\r\n", pF);
	}

	if(pF)
	{
		FILTERPORT_DEBUG("Filter_ReadTS reading\r\n", pu8TSBuf, u32Length);
		u32Size = fread(pu8TSBuf, 1, u32Length, pF);
		if(feof(pF))
		{
			fseek(pF, 0, SEEK_SET);
		}
	}
	//SM_OsThreadSleep(1);
	FILTERPORT_DEBUG("Filter_ReadTS return, s32Size = %d\r\n", u32Size);
	return u32Size;
}

#elif FILTER_READTS_WAY==FILTER_USE_NATIVE_FD
static unsigned char u8Init = 0;
static unsigned char u8TaskRun = 0;

#define MAX_FIFO_NUM	128
#define FIFO_NODE_SIZE		0x4000
unsigned char recvBuffer[MAX_FIFO_NUM][FIFO_NODE_SIZE];
unsigned int recvSize[MAX_FIFO_NUM];
unsigned int u32ReadPos = 0;
unsigned int u32WritePos = 0;
unsigned int u32Readcnt = 8;

static unsigned char u8PathChange = 0;
static volatile unsigned char u8UseLocalFile = 0;
static unsigned int u32BitRate;
static char as8Path[100];
static unsigned int u32ModuleMutex;

unsigned int UsbReadTs_Task(void* pParam)
{
	unsigned int i;
	unsigned int j;
	int ret;
	unsigned int size;
	int readError = 0;
	unsigned int u32Tick;
	FILE* pF = NULL;
	unsigned long long u64FilePos;
	unsigned int u32CurBitRate;
	unsigned long u32TotalSize;
	unsigned int u32StartTick;
	unsigned int u32LoopTime = 0;
	int s32DeltaTick = 0;
	unsigned int u32SleepStartTick;
	unsigned int u32SleepTick;

	FILTERPORT_DEBUG("UsbReadTs_Task entry\r\n");
	SM_OsSetPriority(100);
	u32StartTick = SM_OsTickCountGet();
	while(u8TaskRun)
	{
		if(u8UseLocalFile == 0)
		{
			if(pF)//if not close, system crash when remove udisk 
			{
				fclose(pF);
				pF = NULL;
			}
			
			readError = 0;
			i = u32WritePos%(MAX_FIFO_NUM/u32Readcnt);
			for(j=0; j<u32Readcnt; j++)
			{
				if(readError == 0)
				{
					size = FIFO_NODE_SIZE;
					u32Tick = SM_OsMsecCountGet();
					ret = CamUsbReadTsData(recvBuffer[i*u32Readcnt + j], &size, 3000);
					if(ret > 0)
					{
						recvSize[i*u32Readcnt + j] = size;
						Filter_RecordWorking(recvBuffer[i*u32Readcnt + j] , size);	
						if(SM_OsMsecCountGet()-u32Tick < 60)
						{
							//SM_Printf("UsbReadTs_Task() - read TS data end, size = %d, start tick = %d, end tick = %d, takes time = %d\r\n", 
							//	size, u32Tick, SM_OsMsecCountGet(), SM_OsMsecCountGet()-u32Tick);
						}
						else
						{
							SM_Printf("UsbReadTs_Task() - read TS data too long *******[NOTICE]*******, size = %d, start tick = %d, end tick = %d, takes time = %d\r\n", 
								size, u32Tick, SM_OsMsecCountGet(), SM_OsMsecCountGet()-u32Tick);
						}
					}
					else
					{
						readError = 1;
						SM_OsThreadSleep(500);
						recvSize[i*u32Readcnt + j] = 0;
					}
				}
				else
				{
					recvSize[i*u32Readcnt + j] = 0;
				}
			}
			u32WritePos++;
		}
		else
		{
			if(u8PathChange == 1)
			{
				SM_Printf("UsbReadTs_Task() - new file: %s, u32BitRate: %d\r\n", as8Path, u32BitRate);
				SM_OsSemWait(&u32ModuleMutex, -1);
				u8PathChange = 0;
				if(pF)
				{
					fclose(pF);
				}
				pF = fopen(as8Path, "rb");
				if(pF == NULL)
				{
					SM_Printf_Error("UsbReadTs_Task() -open file failed,  file: %s, u32BitRate: %d\r\n", as8Path, u32BitRate);
				}
				else
				{
					fpos_t pos;
					
					//seek file
					fseek(pF, 0, SEEK_END);
					//u64FilePos = ftell(pF);
					fgetpos(pF, &pos);
					u64FilePos = pos;
					if(u64FilePos > 0 && u32BitRate > 0)
					{
						u32LoopTime = (u64FilePos*8+u32BitRate)/u32BitRate;
						SM_Printf("UsbReadTs_Task() - u32LoopTime = %d\r\n", u32LoopTime);
						if(u32LoopTime > 0)
						{
							u64FilePos = u64FilePos*((SM_OsTickCountGet() - u32StartTick)%u32LoopTime)/u32LoopTime;
							fseek(pF, u64FilePos, SEEK_SET);
						}
						else
						{
							SM_Printf_Error("UsbReadTs_Task() -found file u32LoopTime 0, u32BitRate = %d, u64FilePos = %d, u32LoopTime = %d\r\n", u32BitRate, u64FilePos, u32LoopTime);
							fclose(pF);
							pF = NULL;
						}
					}
					else
					{
						SM_Printf_Error("UsbReadTs_Task() -found file bitrate 0 or filesize 0, u32BitRate = %d, u64FilePos = %d\r\n", u32BitRate, u64FilePos);
						fclose(pF);
						pF = NULL;
					}
				}

				u32CurBitRate = u32BitRate;
				s32DeltaTick = 0;
				SM_OsSemRelease(&u32ModuleMutex);
			}
			
			if(pF)
			{
				u32TotalSize = 0;
				i = u32WritePos%(MAX_FIFO_NUM/u32Readcnt);
				for(j=0; j<u32Readcnt; j++)
				{
					size = FIFO_NODE_SIZE;
					ret = fread(recvBuffer[i*u32Readcnt + j], 1, size, pF);
					if(ret > 0)
					{
						recvSize[i*u32Readcnt + j] = ret;
						u32TotalSize += ret;
					}
					else
					{
						SM_Printf_Error("UsbReadTs_Task() - read local file failed\r\n");
						SM_OsThreadSleep(200);
						readError = 1;
					}
					
					if(feof(pF) != 0)
					{
						fseek(pF, 0, SEEK_SET);
						SM_Printf_Error("UsbReadTs_Task() - read local file looping\r\n");
					}
				}
				u32WritePos++;

				if(u32TotalSize > 5000)//only >5000 bytes, sleep 
				{
					u32SleepTick = u32TotalSize*1000*8/u32CurBitRate;
					u32SleepTick -= s32DeltaTick;
					u32SleepStartTick = SM_OsMsecCountGet();
					SM_OsThreadSleep(u32SleepTick);
					s32DeltaTick = SM_OsMsecCountGet() - u32SleepStartTick - u32SleepTick;
					if((s32DeltaTick>0&&s32DeltaTick>u32SleepTick) || ((s32DeltaTick<0)&&(s32DeltaTick*-1)>u32SleepTick))
					{
						SM_Printf_Error("UsbReadTs_Task() - sleep too long, |s32DeltaTick[%d]| > u32SleepTick, system time may change\r\n", s32DeltaTick);
						s32DeltaTick = 0;
					}
				}
			}
			else
			{
				SM_OsThreadSleep(100);
			}
		}
	}
	return 0;
}

int Filter_CamUsbInit()
{
	int ret;
	unsigned int hThreadId;

	FILTERPORT_DEBUG("Filter_CamUsbInit entry.\r\n");
	SM_OsSemCreate(&u32ModuleMutex, NULL, 1);
	u8TaskRun = 1;
	ret = SM_OsThreadCreate(0, UsbReadTs_Task, NULL, &hThreadId);
	u8Init = 1;
	FILTERPORT_DEBUG("Filter_CamUsbInit return.\r\n");
	
	return ret;
}

int Filter_SetReadTsFromLocalFile(char* ps8Path, unsigned int u32Rate)
{
	int s32Ret = -1;

	SM_OsSemWait(&u32ModuleMutex, -1);
	if(ps8Path)
	{
		SM_Printf("Filter_SetReadTsFromLocalFile) - entry, ps8Path = %s, u32Rate = %d\r\n", ps8Path, u32Rate);
		memset(as8Path, 0, sizeof(as8Path));
		memcpy(as8Path, ps8Path, strlen(ps8Path));
		u32BitRate = u32Rate;
		u8PathChange = 1;
		u8UseLocalFile = 1;
	}
	else
	{
		SM_Printf("Filter_SetReadTsFromLocalFile) - exit local file mode\r\n");
		u8UseLocalFile = 0;
		u8PathChange = 0;
		memset(as8Path, 0, sizeof(as8Path));
	}
	SM_OsSemRelease(&u32ModuleMutex);
	s32Ret = 0;
	
	SM_Printf("Filter_SetReadTsFromLocalFile() return 0\r\n");
	return s32Ret;
}

unsigned int Filter_ReadTS(unsigned char* pu8TSBuf, unsigned int u32Length)
{
	unsigned int ts_len = 0;
	unsigned int read_len = 0;
	unsigned int buffer_len = 0;
	int r;
	unsigned int i, j;

	FILTERPORT_DEBUG("Filter_ReadTS entry pu8TSBuf = %p, u32Length = %d\r\n", pu8TSBuf, u32Length);
	if(u8Init == 0)
	{
		Filter_CamUsbInit();
		SM_OsThreadSleep(100);
	}
	while( u32ReadPos >= u32WritePos)
	{
		SM_OsThreadSleep(10);
	}

	if(u32WritePos - u32ReadPos > MAX_FIFO_NUM/u32Readcnt){
		SM_Printf_Error("Recev buffer too low,it overflowed **************!!");
		u32ReadPos = u32WritePos/*-MAX_FIFO_NUM/u32Readcnt*/-2;
	}

	while(u32ReadPos<u32WritePos && read_len+FIFO_NODE_SIZE/188*188<u32Length)
	{
		i = u32ReadPos%(MAX_FIFO_NUM/u32Readcnt);
		for(j=0; j<u32Readcnt; j++)
		{
			memcpy(pu8TSBuf+read_len, recvBuffer[i*u32Readcnt+j], recvSize[i*u32Readcnt+j]);
			read_len += recvSize[i*u32Readcnt+j];
		}
		u32ReadPos++;
	}

	FILTERPORT_DEBUG("Filter_ReadTS return read_len = %d\r\n", read_len);
	return read_len;
}

#endif

